
#pragma once

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <iostream>

#define PATHNAME "."    //本目录下，感觉无所谓
#define PROI_ID 0x55    //随便给一个

//内存大小
//物理内存是分块的，每块以4kb为单位(分页)
#define MAX_SIZE 4096

//首先搞定唯一标识符key,设计一个统一接口
key_t getKey()
{
    key_t key = ftok(PATHNAME,PROI_ID);
    if(key < 0)//返回值为-1说明获取失败
    {
        std::cout << "get key fail:" << strerror(errno) << std::endl;
        exit(1);
    }
    return key;//返回调用此接口的进程
}

//获得key后，可以通过shmget创建一块共享内存
int createShm(key_t key)
{
    //调用此接口的进程要求必须创建一块新的共享内存
    //需要注意的是，创建共享内存时，注意分配其权限
    int shmId = shmget(key,MAX_SIZE,IPC_CREAT | IPC_EXCL | 0600);
    if(shmId < 0)
    {
        std::cout << "create shmId fail:" << strerror(errno) << std::endl;
        exit(2);
    }
    return shmId;
}

//不需要创建的内存的进程仅需获取标识符即可
int getShm(key_t key)
{
    int shmId = shmget(key,MAX_SIZE,IPC_CREAT);
    if(shmId < 0)
    {
        std::cout << "get shmId fail:" << strerror(errno) << std::endl;
        exit(2);
    }
    return shmId;
}

//然后与进程地址空间挂接
void* attachProcess(int shmId)
{
    void* p = shmat(shmId,nullptr,0);
    if((long long)p == -1L)
    {
        std::cout << "attach fail:" << strerror(errno) << std::endl;
        exit(3);
    }
    return p;
}

//进程退出时，先去关联
void dettachProcess(void* p)
{
    int ret = shmdt(p);
    if(ret < 0)
    {
        std::cout << "dettach fail:" << strerror(errno) << std::endl;
        exit(4);
    }
}

//最后，释放共享内存
void freeMemory(int shmId)
{
    int ret = shmctl(shmId,IPC_RMID,nullptr);
    if(ret < 0)
    {
        std::cout << "free fail:" << strerror(errno) << std::endl;
        exit(5);
    }
}

// 因为两个进程使用共享内存通信，System V没有提供互斥机制
// 说明这两个进程可以同时访问这段内存
// 为了加以限制，在client端向内存写入数据后，给命名管道一个信号
// server端先从管道读取信号，如果确实有信号，再从内存中读取数据


#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>
#define PIPE_NAME "./named_pipe"

//server端创建命名管道
void createPipe()
{
    umask(0);
    int n = mkfifo(PIPE_NAME,0600);
    if(n < 0)
    {
        std::cout << "mkfifo fail:" << strerror(errno) << std::endl;
        exit(6);
    }
}

//server端释放命名管道
void freePipe()
{
    int n = unlink(PIPE_NAME);
    if(n < 0)
    {
        std::cout << "unlink fail:" << strerror(errno) << std::endl;
        exit(7);
    }
}